using System;
using System.IO;
using System.Xml;
using System.Xml.XPath;
using NDesk.Options;

namespace NetDiagnosingToolkit 
{
    public static class AdoNetTraceReader
    {  
        private const int TabSize = 4;
        private const int LineProlog = 8;
        
        public static void Main(String[] args)
        {
            bool show_help = false;
            String inpath = null, outpath = null, pid = null;
            
            var p = new OptionSet () {
                { "i=|input", "XML input file (generated by tracerpt)", v => inpath = v },
                { "o=|output", "output file that will contain parsed traces", v => outpath = v },
                { "p=|pid", "PID of the process by which traces will be filtered", v => pid = v },
                { "h|help",  "show this message and exit", v => show_help = v != null },
            };
            
            try {
                p.Parse (args);
            }
            catch (OptionException e) {
                Console.Write("AdoNetTraceReader: ");
                Console.WriteLine(e.Message);
                Console.WriteLine("Try `AdoNetTraceReader --help' for more information.");
                return;
            }
            
            if (show_help || inpath == null || outpath == null) {
                Console.WriteLine("USAGE: AdoNetTraceReader [options]");
                Console.WriteLine();
                Console.WriteLine("Options:");
                p.WriteOptionDescriptions(Console.Out);
                return;
            }

            using (var writer = new StreamWriter(outpath)) {
                // get nodes from xml and process each node
                var xdoc = new XPathDocument(inpath);
                var xnav = xdoc.CreateNavigator();
                // prepare namespace
                var nsm = new XmlNamespaceManager(xnav.NameTable);
                nsm.AddNamespace("ns", "http://schemas.microsoft.com/win/2004/08/events/event");

                int currIndent = 0;
                if (String.IsNullOrEmpty(pid)) {
                  foreach (XPathNavigator xn in xnav.Select("Events/ns:Event/ns:EventData/ns:Data[@Name=\"msgStr\"]", nsm)) {
                      WriteNode(writer, xn, ref currIndent);
                  }
                } else {
                    // first filter processes
                    foreach (XPathNavigator pxn in xnav.Select("Events/ns:Event[ns:System/ns:Execution[@ProcessID=\"" + pid + "\"]]", nsm)) {
                        foreach (XPathNavigator xn in pxn.Select("ns:EventData/ns:Data[@Name=\"msgStr\"]", nsm)) {
                            WriteNode(writer, xn, ref currIndent);
                        }
                    }
                }
            }
        }
        
        private static void WriteNode(StreamWriter writer, XPathNavigator xn, ref int currIndent) {
          String line = xn.Value;
          if (String.IsNullOrEmpty(line))
              return;

          String operation = null;
          if (line.StartsWith("enter", StringComparison.Ordinal)) {
              operation = "START ";
          } else if (line.StartsWith("leave", StringComparison.Ordinal)) {
              operation = "END ";
          }
          if (operation != null) {
              // substract the string and split it
              var splits = line.Split(new[] { '_', ' ' }, StringSplitOptions.RemoveEmptyEntries);
              if (splits.Length >= 2) {
                  currIndent = Int32.Parse(splits[1]) * TabSize;
                  writer.Write("".PadLeft(currIndent) + operation + line.Substring(LineProlog));
                  return;
              }
          }
          // just output the line
          writer.Write("".PadLeft(currIndent) + line);
        }
    }
}